/*****************************************************************************
*                     Organizaci�n de Datos - 75.06                          *
*                         C�tedra Lic. Servetto                              *
*            Facultad de Ingenier�a - Universidad de Buenos Aires            *
******************************************************************************/ 

#include "Archivo.h"
#include "Operacion.h"
#include "ConversorDeStreams.h"
#include <iostream>
#include <sstream>
#include <string>

using namespace std;

namespace Comun
{
	
	/***************************************/
	/* DESDE DATOS CONCRETOS HACIA STREAMS */
	/***************************************/
	
	/// Inserta la representacion del tipo de dato nulo en la salida estandar.
	/// @param salida Stream donde se guardara dato.
	void ConversorDeStreams::VoidAStream(stringstream* salida)
	{
		*salida << "void" << endl;
	};
	
	/// Inserta la represetacion de un entero en la salida estandar.
	/// @param entero Numero entero a guardar en el stream.
	/// @param salida Stream donde se guardara dato.
	void ConversorDeStreams::EnteroAStream(int entero, stringstream* salida)
	{
		*salida << entero << endl;
	};
	
	/// Inserta la representacion de un booleano en la salida estandar.
	/// @param booleano Booleano a guardar en el stream.
	/// @param salida Stream donde se guardara dato.
	void ConversorDeStreams::BooleanoAStream(bool booleano, stringstream* salida)
	{
		*salida << booleano << endl;
	};
	
	/// Inserta la representacion de una cadena en la salida estandar.
	/// @param cadena String a guardar en el stream.
	/// @param salida Stream donde se guardara dato.
	void ConversorDeStreams::CadenaAStream(string cadena, stringstream* salida)
	{
		*salida << cadena.c_str() << endl;
	};
	
	/// Inserta la representacion de un operacion en la salida estandar.
	/// @param operacion Operacion a guardar en el stream.
	/// @param salida Stream donde se guardara dato.
	void ConversorDeStreams::OperacionAStream(Operacion operacion, stringstream* salida)
	{
		*salida << ParserDeOperacion::Parsear(operacion).c_str() << endl;
	};
	
	/// Inserta la representacion de un grupo, con sus atributos, registros y datos, en la salida estandar.
	/// @param grupo Comun::Grupo a ser representado en el stream.
	/// @param salida Stream donde se guardara dato.
	void ConversorDeStreams::DatosDeGrupoAStream(Grupo& grupo, stringstream* salida)
	{
		///////////////////////////////////////////////////////////////////////
		//
		// Formato del grupo serializado
		// *****************************
		//
		// (grupoId, cont_atributos, (nombre, tipo, valor)+, cont_registros, (tam_registro, cont_atr, (nombre, tipo, valor)+)+)
		//
		// ID de grupo
		// Cantidad de atributos del grupo
		// Nombre del atributo 1 del grupo
		// Tipo del atributo 1 del grupo
		// Valor del atributo 1 del grupo
		// ...
		// Nombre del atributo N del grupo
		// Tipo del atributo N del grupo
		// Valor del atributo N del grupo
		// Cantidad de registro del grupo
		// Tamaño del registro 1 del grupo
		// Cantidad de atributos del registro 1 del grupo
		// Nombre del atributo 1 del registro 1 del grupo
		// Tipo del atributo 1 del registro 1 del grupo
		// Valor del atributo 1 del registro 1 del grupo
		// ...
		// Nombre del atributo N del registro 1 del grupo
		// Tipo del atributo N del registro 1 del grupo
		// Valor del atributo N del registro 1 del grupo
		// ...
		// Tamaño del registro M del grupo
		// Cantidad de atributos del registro M del grupo
		// Nombre del atributo 1 del registro M del grupo
		// Tipo del atributo 1 del registro M del grupo
		// Valor del atributo 1 del registro M del grupo
		// ...
		// Nombre del atributo N del registro M del grupo
		// Tipo del atributo N del registro M del grupo
		// Valor del atributo N del registro M del grupo
		//
		// Ejemplo
		// *******
		//
		// 0
		// 1
		// Nombre
		// Cadena
		// Valor
		// 1
		// 0
		// 1
		// NumeroSeleccionado
		// Entero
		// 0
		//
		///////////////////////////////////////////////////////////////////////
		
		
		*salida << grupo.GrupoId << endl;
		
		*salida << grupo.Atributos.size() << endl;
		for(IteradorDeAtributos i = grupo.Atributos.begin(); i != grupo.Atributos.end(); ++i)
		{
			Atributo atributo = *i;
			*salida << atributo.Nombre.c_str() << endl;
			*salida << ParserDeTipoRegistro::Parsear(atributo.Tipo).c_str() << endl;
			*salida << atributo.Valor.c_str() << endl;
		}
	
		*salida << grupo.Registros.size() << endl;
		for(IteradorDeRegistros j = grupo.Registros.begin(); j != grupo.Registros.end(); ++j)
		{
			Registro registro = *j;
			*salida << registro.Tamanio << endl;
			*salida << registro.Atributos.size() << endl;
			for(IteradorDeAtributos i = registro.Atributos.begin(); i != registro.Atributos.end(); ++i)
			{
				Atributo atributo = *i;
				*salida << atributo.Nombre.c_str() << endl;
				*salida << ParserDeTipoRegistro::Parsear(atributo.Tipo).c_str() << endl;
				*salida << atributo.Valor.c_str() << endl;
			}
		}
	};
	
	/// Inserta la representacion un la descripcion de metadatos de archivo en la salida estandar.
	/// @param MetadatosArchivo Comun::MetadatosArchivos a ser representado en el stream.
	/// @param salida Stream donde se guardara dato.
	void ConversorDeStreams::MetadatosDeArchivoAStream(MetadatosArchivo& metadatosArchivo, stringstream* salida)
	{
		*salida << metadatosArchivo.Nombre.c_str() << endl;
		*salida << metadatosArchivo.TamanioBloque << endl;
		
		*salida << metadatosArchivo.MetadatosGrupo->MetadatosAtributos.size() << endl;
		for(IteradorDeMetadatosDeAtributos i = metadatosArchivo.MetadatosGrupo->MetadatosAtributos.begin(); i != metadatosArchivo.MetadatosGrupo->MetadatosAtributos.end(); ++i)
		{
			MetadatosAtributo metadatosAtributo = *i;
			*salida << metadatosAtributo.Nombre.c_str() << endl;
			*salida << ParserDeTipoRegistro::Parsear(metadatosAtributo.Tipo).c_str() << endl;
		}
		
		*salida << metadatosArchivo.MetadatosGrupo->MetadatosRegistro->MetadatosAtributos.size() << endl;
		for(IteradorDeMetadatosDeAtributos i = metadatosArchivo.MetadatosGrupo->MetadatosRegistro->MetadatosAtributos.begin(); i != metadatosArchivo.MetadatosGrupo->MetadatosRegistro->MetadatosAtributos.end(); ++i)
		{
			MetadatosAtributo metadatosAtributo = *i;
			*salida << metadatosAtributo.Nombre.c_str() << endl;
			*salida << ParserDeTipoRegistro::Parsear(metadatosAtributo.Tipo).c_str() << endl;
		}
	};
	
	/***********************************/
	/* DESDE STREAMS A DATOS CONCRETOS */
	/***********************************/
	
	/// Lee la representacion de un entero de la entrada estandar y lo devuelve.
	/// @param entrada Stream con los datos de entrada.
	/// @return Entero con el valor parseado.
	int ConversorDeStreams::StreamAEntero(stringstream* entrada)
	{
		// Leo la entrada como entero.
		int resultado;
		*entrada >> resultado;
		
		// Devuelvo el entero.
		return resultado;
	};

	/// Lee la representacion de un booleano de la entrada estandar y lo devuelve.
	/// @param entrada Stream con los datos de entrada.
	/// @return Booleano con el valor parseado.
	bool ConversorDeStreams::StreamABooleano(stringstream* entrada)
	{
		// Leo la entrada como booleano.
		bool resultado;
		*entrada >> resultado;
		
		// Devuelvo el booleano
		return resultado;
	};

	/// Lee la representacion de una cadena de la entrada estandar y la devuelve.
	/// @param entrada Stream con los datos de entrada.
	/// @return String con el valor parseado.
	string ConversorDeStreams::StreamACadena(stringstream* entrada)
	{
		// Leo la entrada como string.
		string resultado;
		*entrada >> resultado;
		
		// Devuelvo el string.
		return resultado;
	};

	/// Lee la representacion de una operacion de la entrada estandar y la devulve.
	/// @param entrada Stream con los datos de entrada.
	/// @return Operacion representada en el stream.
	Operacion ConversorDeStreams::StreamAOperacion(stringstream* entrada)
	{
		// Leo la entrada como string.
		string resultado;
		*entrada >> resultado;
		
		// Parseo la operacion y la devuelvo.
		return ParserDeOperacion::Parsear(resultado);
	};

	/// Lee la representaicon de un grupo con sus atributos, registros y datos de la entrada estandar y lo devuelve.
	/// @param entrada Stream con los datos de entrada.	
	/// @return Comun::Grupo cargado con los datos del stream.
	Grupo& ConversorDeStreams::StreamADatosDeGrupo(stringstream* entrada)
	{
		// Creo un grupo vacio.
		Grupo& grupo = *(new Grupo());
		
		// Leo el grupoId del stream.
		*entrada >> grupo.GrupoId;
		
		// Leo los atributos del grupo del stream.
		int cantidadAtributos;
		*entrada >> cantidadAtributos;
		for(int i = 0; i < cantidadAtributos; ++i)
		{
			Atributo& atributo = *(new Atributo());
			*entrada >> atributo.Nombre;
			string tipoAtributo;
			*entrada >> tipoAtributo;
			atributo.Tipo = ParserDeTipoRegistro::Parsear(tipoAtributo);
			getline(*entrada, atributo.Valor); // Emula un get() de un solo caracter;
			getline(*entrada, atributo.Valor);
			grupo.Atributos.push_back(atributo);
		}
		
		// Leo los registros del stream.
		int cantidadRegistros;
		*entrada >> cantidadRegistros;
		for(int j = 0; j < cantidadRegistros; ++j)
		{
			// Creo un registro vacio.
			Registro& registro = *(new Registro());
			
			// Leo los datos del registro.
			int tamanioDelRegistro;
			*entrada >> tamanioDelRegistro;
			registro.Tamanio = tamanioDelRegistro;
			
			// Leo los atributos del registro del stream.
			int cantidadAtributosDelRegistro;
			*entrada >> cantidadAtributosDelRegistro;
			for(int i = 0; i < cantidadAtributosDelRegistro; ++i)
			{
				Atributo& atributo = *(new Atributo());
				*entrada >> atributo.Nombre;
				string tipoAtributo;
				*entrada >> tipoAtributo;
				atributo.Tipo = ParserDeTipoRegistro::Parsear(tipoAtributo);
				getline(*entrada, atributo.Valor); // Emula un get() de un solo caracter;
				getline(*entrada, atributo.Valor);
				registro.Atributos.push_back(atributo);
			}
			
			// Agrego el registro con datos a la coleccion de registros.
			grupo.Registros.push_back(registro);
		}
		
		// Devuelvo el grupo con los datos cargados.
		return grupo;
	};

	/// Lee la representacion un la metadata de un archivo de la entrada estandar y la devuelve.
	/// @param entrada Stream con los datos de entrada.
	/// @return Comun::MetadatosArchivos cargados con la metadata del stream.
	MetadatosArchivo& ConversorDeStreams::StreamAMetadatosDeArchivo(stringstream* entrada)
	{
		string nombreClase;
		*entrada >> nombreClase;

		MetadatosArchivo& metadatosArchivo = *(new MetadatosArchivo(nombreClase));

		int tamanioBloque;
		*entrada >> tamanioBloque;
		metadatosArchivo.TamanioBloque = tamanioBloque;

		int cantidadAtributos;
		*entrada >> cantidadAtributos;
		metadatosArchivo.MetadatosGrupo = new MetadatosGrupo();
		for(int i = 0; i < cantidadAtributos; ++i)
		{
			string nombreAtributo;
			*entrada >> nombreAtributo;
			string tipoAtributo;
			*entrada >> tipoAtributo;
			MetadatosAtributo& metadatosAtributo = *(new MetadatosAtributo(nombreAtributo,ParserDeTipoRegistro::Parsear(tipoAtributo)));
			metadatosArchivo.MetadatosGrupo->MetadatosAtributos.push_back(metadatosAtributo);
		}

		int cantidadAtributosDeLosRegistros;
		*entrada >> cantidadAtributosDeLosRegistros;
		metadatosArchivo.MetadatosGrupo->MetadatosRegistro = new MetadatosRegistro();
		for(int j = 0; j < cantidadAtributosDeLosRegistros; ++j)
		{
			string nombreAtributo;
			*entrada >> nombreAtributo;
			string tipoAtributo;
			*entrada >> tipoAtributo;
			MetadatosAtributo& metadatosAtributo = *(new MetadatosAtributo(nombreAtributo,ParserDeTipoRegistro::Parsear(tipoAtributo)));
			metadatosArchivo.MetadatosGrupo->MetadatosRegistro->MetadatosAtributos.push_back(metadatosAtributo);
		}

		return metadatosArchivo;
	};

}
